#pragma once
#include <iostream>
#include <mutex>

namespace smart_ptr
{
    template <class T>
    class auto_ptr
    {
    public:
        auto_ptr(T *ptr)
            : _ptr(ptr)
        {
        }

        ~auto_ptr()
        {
            if (_ptr != nullptr)
                delete _ptr;
        }

        auto_ptr(auto_ptr &old_ptr)
            : _ptr(old_ptr._ptr)
        {
            old_ptr._ptr = nullptr;
        }

        auto_ptr &operator=(auto_ptr &new_ptr)
        {
            if (this != &new_ptr)
            {
                delete _ptr;
                _ptr = new_ptr._ptr;
                new_ptr._ptr = nullptr;
            }
            return *this;
        }

    public:
        T *operator->()
        {
            return _ptr;
        }

        T &operator*()
        {
            return *_ptr;
        }

    private:
        T *_ptr;
    };

    template <class T>
    class unique_ptr
    {
    public:
        unique_ptr(T *ptr)
            : _ptr(ptr)
        {
        }

        ~unique_ptr()
        {
            if (_ptr != nullptr)
                delete _ptr;
            _ptr = nullptr;
        }

    public:
        T *operator->()
        {
            return _ptr;
        }

        T &operator*()
        {
            return *_ptr;
        }

    private:
        unique_ptr(const unique_ptr &) = delete;
        unique_ptr &operator=(const unique_ptr &) = delete;

    private:
        T *_ptr;
    };

    template <class T>
    class shared_ptr
    {
    public:
        shared_ptr(T *ptr)
            : _ptr(ptr), _cnt(new int(1)), _mtx(new std::mutex)
        {
        }

        ~shared_ptr()
        {
            ReleaseRef();
        }

        shared_ptr(const shared_ptr &old_ptr)
            : _ptr(old_ptr._ptr), _cnt(old_ptr._cnt), _mtx(old_ptr._mtx)
        {
            AddRef();
        }

        shared_ptr &operator=(const shared_ptr &old_ptr)
        {
            if (this != &old_ptr)
            {
                ReleaseRef();
                _ptr = old_ptr._ptr;
                _cnt = old_ptr._cnt;
                _mtx = old_ptr._mtx;
                AddRef();
            }

            return *this;
        }

    public:
        T *operator->()
        {
            return _ptr;
        }

        T &operator*()
        {
            return *_ptr;
        }

        int Cnt()
        {
            return *_cnt;
        }
    private:
        void AddRef()
		{
			_mtx->lock();
			(*_cnt)++;
			_mtx->unlock();
		}
        void ReleaseRef()
		{
			_mtx->lock();
			bool flag = false;
			if (--(*_cnt) == 0) //将管理的资源对应的引用计数--
			{
				if (_ptr != nullptr)
				{
					delete _ptr;
					_ptr = nullptr;
				}
				delete _cnt;
				_cnt = nullptr;
				flag = true;
			}
			_mtx->unlock();
			if (flag == true)
			{
				delete _mtx;
			}
		}

    private:
        T *_ptr;
        int *_cnt;
        std::mutex* _mtx;
    };
}